package com.glwan.fixedassetapp.helper;

import android.app.Activity;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import com.glwan.fixedassetapp.MainActivity;
import com.glwan.fixedassetapp.R;
import com.glwan.fixedassetapp.handler.ReaderResponseInterfaces;
import com.glwan.fixedassetapp.model.RFIDTagData;
import com.glwan.fixedassetapp.utils.CommonUtil;
import com.zebra.rfid.api3.ACCESS_OPERATION_CODE;
import com.zebra.rfid.api3.ACCESS_OPERATION_STATUS;
import com.zebra.rfid.api3.Antennas;
import com.zebra.rfid.api3.BEEPER_VOLUME;
import com.zebra.rfid.api3.ENUM_TRANSPORT;
import com.zebra.rfid.api3.ENUM_TRIGGER_MODE;
import com.zebra.rfid.api3.HANDHELD_TRIGGER_EVENT_TYPE;
import com.zebra.rfid.api3.HANDHELD_TRIGGER_TYPE;
import com.zebra.rfid.api3.INVENTORY_STATE;
import com.zebra.rfid.api3.InvalidUsageException;
import com.zebra.rfid.api3.OperationFailureException;
import com.zebra.rfid.api3.RFIDReader;
import com.zebra.rfid.api3.ReaderDevice;
import com.zebra.rfid.api3.Readers;
import com.zebra.rfid.api3.RfidEventsListener;
import com.zebra.rfid.api3.RfidReadEvents;
import com.zebra.rfid.api3.RfidStatusEvents;
import com.zebra.rfid.api3.SESSION;
import com.zebra.rfid.api3.SL_FLAG;
import com.zebra.rfid.api3.START_TRIGGER_TYPE;
import com.zebra.rfid.api3.STATUS_EVENT_TYPE;
import com.zebra.rfid.api3.STOP_TRIGGER_TYPE;
import com.zebra.rfid.api3.TagData;
import com.zebra.rfid.api3.TriggerInfo;

import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

public class ZebraReaderHelper implements RFIDDeviceInterface, Readers.RFIDReaderEventHandler {

    private static final String TAG = "Zebra";

    private static ZebraReaderHelper zebraReaderHelper;

    private static Readers readers;
    private static ArrayList<ReaderDevice> availableRFIDReaderList;
    private static ReaderDevice readerDevice;
    public RFIDReader reader;
    public Timer tbeep;
    // In case of RFD8500 change reader name with intended device below from list of paired RFD8500
//    String readerName = "RFD8500123";
    boolean needSound = true;
    String readername = "RFD8500123";
    private int MAX_POWER = 270;
    private EventHandler eventHandler;
    private ToneGenerator toneGenerator;
    private Activity activity;
    private boolean isConnected = false;
    //0--inventory 1--locate
    private int inventoryMode = 0;
    //tagPattern
    private String tagPattern;
    private boolean beepON = false;


//    public boolean initSDK()) {
//        Log.d(TAG, "InitSDK");
//        try {
//            if(readers == null)
//                readers = new Readers(activity, ENUM_TRANSPORT.ALL);
//            availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
//            GetAvailableReader();
//        } catch (InvalidUsageException e) {
//            e.printStackTrace();
//        }
//        return connect(activity);
//    }

    private ZebraReaderHelper() {

    }

    public static ZebraReaderHelper getInstance(Activity activity) {
        if (zebraReaderHelper == null) {
            zebraReaderHelper = new ZebraReaderHelper();
        }
        zebraReaderHelper.activity = activity;
        return zebraReaderHelper;
    }

    private synchronized void GetAvailableReader() throws InvalidUsageException {
        Log.d(TAG, "GetAvailableReader");
        if (readers != null) {
//            readers.attach(this);
            if (readers.GetAvailableRFIDReaderList() != null) {
                availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
                if (availableRFIDReaderList.size() != 0) {
                    // if single reader is available then connect it
                    if (availableRFIDReaderList.size() == 1) {
                        readerDevice = availableRFIDReaderList.get(0);
                        reader = readerDevice.getRFIDReader();
                    } else {
                        // search reader specified by name
                        for (ReaderDevice device : availableRFIDReaderList) {
                            if (device.getName().equals(readername)) {
                                readerDevice = device;
                                reader = readerDevice.getRFIDReader();
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public synchronized boolean connect() {
        Log.d(TAG, "InitSDK");
        try {
            if (readers == null)
                readers = new Readers(activity, ENUM_TRANSPORT.ALL);
            availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
            GetAvailableReader();
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        }
        if (reader != null) {
            Log.d(TAG, "connect " + reader.getHostName());

            try {
                if (!reader.isConnected()) {
                    // Establish connection to the RFID Reader
                    reader.connect();

                    ConfigureReader();
                    CommonUtil.toastShow(activity, "connect " + reader.getHostName(), CommonUtil.SUCCESS, true);
                    if (reader.getHostName().contains("RFD4030"))
                        needSound = false;
                    isConnected = true;
                    return true;
                }

            } catch (InvalidUsageException e) {
                CommonUtil.toastShow(activity, activity.getString(R.string.connectFail) + reader.getHostName(), CommonUtil.ERROR, true);
                e.printStackTrace();
            } catch (OperationFailureException e) {
                e.printStackTrace();
                Log.d(TAG, "OperationFailureException " + e.getVendorMessage());
                String des = e.getResults().toString();
                return false;
            }
        }
        return false;
    }

    private void ConfigureReader() {
        Log.d(TAG, "ConfigureReader " + reader.getHostName());
        if (reader.isConnected()) {
            TriggerInfo triggerInfo = new TriggerInfo();
            triggerInfo.StartTrigger.setTriggerType(START_TRIGGER_TYPE.START_TRIGGER_TYPE_IMMEDIATE);
            triggerInfo.StopTrigger.setTriggerType(STOP_TRIGGER_TYPE.STOP_TRIGGER_TYPE_IMMEDIATE);
            try {
                // receive events from reader
                if (eventHandler == null)
                    eventHandler = new EventHandler();
                reader.Events.removeEventsListener(eventHandler);
                reader.Events.addEventsListener(eventHandler);
                // HH event
                reader.Events.setHandheldEvent(true);
                // tag event with tag data
                reader.Events.setTagReadEvent(true);
                reader.Events.setAttachTagDataWithReadEvent(false);
                // set trigger mode as rfid so scanner beam will not come
                reader.Config.setTriggerMode(ENUM_TRIGGER_MODE.RFID_MODE, true);
                // set start and stop triggers
                reader.Config.setStartTrigger(triggerInfo.StartTrigger);
                reader.Config.setStopTrigger(triggerInfo.StopTrigger);
                // power levels are index based so maximum power supported get the last one
                MAX_POWER = reader.ReaderCapabilities.getTransmitPowerLevelValues().length - 1;
                // set antenna configurations
                Antennas.AntennaRfConfig config = reader.Config.Antennas.getAntennaRfConfig(1);
                config.setTransmitPowerIndex(MAX_POWER);
                config.setrfModeTableIndex(0);
                config.setTari(0);
                reader.Config.Antennas.setAntennaRfConfig(1, config);


                // Set the singulation control
//                Antennas.SingulationControl s1_singulationControl = reader.Config.Antennas.getSingulationControl(1);
//                s1_singulationControl.setSession(SESSION.SESSION_S0);
//                s1_singulationControl.Action.setInventoryState(INVENTORY_STATE.INVENTORY_STATE_A);
//                s1_singulationControl.Action.setSLFlag(SL_FLAG.SL_ALL);
//                reader.Config.Antennas.setSingulationControl(1, s1_singulationControl);
                setSession();

                //set beeper
                reader.Config.setBeeperVolume(BEEPER_VOLUME.HIGH_BEEP);

//                reader.Config.getBeeperVolume();

                toneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF, 100);

                // delete any prefilters
                reader.Actions.PreFilters.deleteAll();
                //
            } catch (InvalidUsageException | OperationFailureException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public synchronized boolean disconnect() {
        boolean status = false;
        Log.d(TAG, "disconnect " + reader);
        try {
            if (reader != null) {
                reader.Events.removeEventsListener(eventHandler);
                reader.disconnect();
//                context.runOnUiThread(new Runnable() {
//                    @Override
//                    public void run() {
//                        editText.setText("Disconnected");
//                    }
//                });
                status = true;
                isConnected = false;
            }
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return status;
    }

    @Override
    public boolean setPower(int power) {
        boolean status = false;
        try {
            // get the configuration
            Antennas.AntennaRfConfig antennaRfConfig = reader.Config.Antennas.getAntennaRfConfig(1);
            antennaRfConfig.setrfModeTableIndex(0);
            antennaRfConfig.setTari(0);
            antennaRfConfig.setTransmitPowerIndex(power);
            // set the configuration
            reader.Config.Antennas.setAntennaRfConfig(1, antennaRfConfig);
            status = true;
        } catch (OperationFailureException | InvalidUsageException ex) {
            Log.d(TAG, (" Antenna configuration failed " + ex.getMessage()));
        }
        return status;
    }

    @Override
    public Integer getPower() {
        int transmitPowerIndex = 0;
        try {
            Antennas.AntennaRfConfig antennaRfConfig = reader.Config.Antennas.getAntennaRfConfig(1);
            transmitPowerIndex = antennaRfConfig.getTransmitPowerIndex();
        } catch (InvalidUsageException | OperationFailureException e) {
            e.printStackTrace();
        }
        return transmitPowerIndex;
    }

    @Override
    public boolean setSession(int session) {
        // Set Singulation Control for the antenna 1
        boolean status = false;
        SESSION sessionModel = SESSION.SESSION_S0;
        if (session == SESSION.SESSION_S1.getValue()) {
            sessionModel = SESSION.SESSION_S1;
        } else if (session == SESSION.SESSION_S2.getValue()) {
            sessionModel = SESSION.SESSION_S2;
        } else if (session == SESSION.SESSION_S3.getValue()) {
            sessionModel = SESSION.SESSION_S3;
        }

        Antennas.SingulationControl singulationControl;

        try {
            singulationControl = reader.Config.Antennas.getSingulationControl(1);
            singulationControl.setSession(sessionModel);
            reader.Config.Antennas.setSingulationControl(1, singulationControl);
            status = true;
        } catch (InvalidUsageException | OperationFailureException e) {
            e.printStackTrace();
        }

        return status;
    }

    @Override
    public Integer getSession() {
        // Get Singulation Control for the antenna 1
        int session = 0;
        try {
            Antennas.SingulationControl singulationControl = reader.Config.Antennas.getSingulationControl(1);
            session = singulationControl.getSession().getValue();
        } catch (InvalidUsageException | OperationFailureException e) {
            e.printStackTrace();
        }


        return session;
    }

    @Override
    public boolean isConnected() {
        return isConnected;
    }

    @Override
    public void changeInventoryMode(int mode, String tagPattern) {
        this.inventoryMode = mode;
        this.tagPattern = tagPattern;
    }

    @Override
    public void RFIDReaderAppeared(ReaderDevice readerDevice) {

    }

    @Override
    public void RFIDReaderDisappeared(ReaderDevice readerDevice) {

    }

    public String setSession() {
        // check reader connection
        if (!reader.isConnected())
            return "Not connected";
        // Set the singulation control to S1 which will read each tag once only
        try {
            Antennas.SingulationControl s1_singulationControl = reader.Config.Antennas.getSingulationControl(1);
            s1_singulationControl.setSession(SESSION.SESSION_S0);
            s1_singulationControl.Action.setInventoryState(INVENTORY_STATE.INVENTORY_STATE_A);
            s1_singulationControl.Action.setSLFlag(SL_FLAG.SL_ALL);
            reader.Config.Antennas.setSingulationControl(1, s1_singulationControl);
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
            return e.getResults().toString() + " " + e.getVendorMessage();
        }
        return "Session set to S1";
    }

    public void handleTriggerPress(boolean pressed) {
        if (pressed) {
            if (activity instanceof ReaderResponseInterfaces.ResponseStatusHandler) {
                ((ReaderResponseInterfaces.ResponseStatusHandler) activity).handleStatusResponse(true);
            }
            startInventory();
        } else {
            if (activity instanceof ReaderResponseInterfaces.ResponseStatusHandler) {
                ((ReaderResponseInterfaces.ResponseStatusHandler) activity).handleStatusResponse(false);
            }
            stopInventory();
        }
    }

    @Override
    public synchronized boolean startInventory() {
        // check reader connection
        boolean status = false;
        if (!reader.isConnected())
            return status;
        try {
            if (inventoryMode == 1) {
                reader.Actions.TagLocationing.Perform(tagPattern, null, null);
            } else {
                reader.Actions.purgeTags();
                reader.Actions.Inventory.perform();
            }
            status = true;
//            Toast.makeText(this, "开始盘点", Toast.LENGTH_SHORT).show();
        } catch (InvalidUsageException | OperationFailureException e) {
            e.printStackTrace();
        }
        return status;
    }

    @Override
    public synchronized boolean stopInventory() {

        boolean status = false;

        // check reader connection
        if (!reader.isConnected())
            return status;
        try {
            if (inventoryMode == 1) {
                reader.Actions.TagLocationing.Stop();
            } else {
                reader.Actions.Inventory.stop();
            }
            status = true;
//            Toast.makeText(this, "关闭盘点", Toast.LENGTH_SHORT).show();
        } catch (InvalidUsageException | OperationFailureException e) {
            e.printStackTrace();
        }
        return status;
    }

    public void beep() {
        if (toneGenerator != null) {
            int toneType = ToneGenerator.TONE_PROP_BEEP;
            toneGenerator.startTone(toneType);

            toneGenerator.stopTone();
        }
    }

    public void startBeepingTimer() {
//        if (beeperVolume != BEEPER_VOLUME.QUIET_BEEP) {
        if (!beepON) {
            beepON = true;
            beep();
            if (tbeep == null) {
                TimerTask task = new TimerTask() {
                    @Override
                    public void run() {
                        stopBeepingTimer();
                        beepON = false;
                    }
                };
                tbeep = new Timer();
                tbeep.schedule(task, 10);
            }
        }
//        }
    }

    /**
     * method to stop timer
     */
    public void stopBeepingTimer() {
        if (tbeep != null && toneGenerator != null) {
            toneGenerator.stopTone();
            tbeep.cancel();
            tbeep.purge();
        }
        tbeep = null;
    }

    public class EventHandler implements RfidEventsListener {

        // Read Event Notification
        public void eventReadNotify(RfidReadEvents e) {
            // Recommended to use new method getReadTagsEx for better performance in case of large tag population
            TagData[] myTags = reader.Actions.getReadTags(100);
            if (myTags != null) {
                StringBuilder stringBuilder = new StringBuilder();
                for (TagData myTag : myTags) {

                    RFIDTagData rfidTagData = new RFIDTagData();
                    rfidTagData.setAntennaID(myTag.getAntennaID());
                    rfidTagData.setEpc(myTag.getTagID());
                    rfidTagData.setRssi(myTag.getPeakRSSI());
                    if (inventoryMode == 1) {
                        if (myTag.isContainsLocationInfo()) {
                            short relativeDistance = myTag.LocationInfo.getRelativeDistance();
                            rfidTagData.setRelativeDistance(relativeDistance);
                            if (relativeDistance > 0) {
                                if (needSound)
                                    startBeepingTimer();
                            }
                        }
                    } else {
                        Log.d(TAG, "Tag ID " + myTag.getTagID());
                        if (needSound)
                            startBeepingTimer();
                        stringBuilder.append(myTag.getTagID()).append("\r\n");
                        if (myTag.getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_READ &&
                                myTag.getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) {
                            if (myTag.getMemoryBankData().length() > 0) {
                                Log.d(TAG, " Mem Bank Data " + myTag.getMemoryBankData());
                            }
                        }
                    }


                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {

                            activity.runOnUiThread(() -> {
                                if (activity instanceof ReaderResponseInterfaces.ResponseTagHandler) {
                                    ((ReaderResponseInterfaces.ResponseTagHandler) activity).handleResponse(rfidTagData);
//                                editText.append(stringBuilder.toString());
                                }
                            });
                            return null;
                        }
                    }.execute();

                }

//                Toast.makeText(MainActivity.this, stringBuilder.toString(), Toast.LENGTH_SHORT).show();

                // possibly if operation was invoked from async task and still busy
                // handle tag data responses on parallel thread thus THREAD_POOL_EXECUTOR

            }
        }

        // Status Event Notification
        public void eventStatusNotify(RfidStatusEvents rfidStatusEvents) {
            Log.d(TAG, "Status Notification: " + rfidStatusEvents.StatusEventData.getStatusEventType());
            if (rfidStatusEvents.StatusEventData.getStatusEventType() == STATUS_EVENT_TYPE.HANDHELD_TRIGGER_EVENT) {
                if (rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldEvent() == HANDHELD_TRIGGER_EVENT_TYPE.HANDHELD_TRIGGER_PRESSED && rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldTriggerType().ordinal == HANDHELD_TRIGGER_TYPE.HANDHELD_TRIGGER_RFID.ordinal) {
                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {
                            handleTriggerPress(true);
                            return null;
                        }
                    }.execute();
                }
                if (rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldEvent() == HANDHELD_TRIGGER_EVENT_TYPE.HANDHELD_TRIGGER_RELEASED && rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldTriggerType().ordinal == HANDHELD_TRIGGER_TYPE.HANDHELD_TRIGGER_RFID.ordinal) {
                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {
                            handleTriggerPress(false);
                            return null;
                        }
                    }.execute();
                }
            }

        }
    }


}
